﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Net.Sockets;
using System.Net;
using System.Threading;

namespace WebServer
{
    class Server
    {
        public bool running = false;
        private int timeout = 8;
        private Encoding charEncoder = Encoding.UTF8;
        private Socket serverSocket;
        private string contentPath;

        private Dictionary<string, string> extensions = new Dictionary<string, string>()
        { 
            { "htm", "text/html" },
            { "html", "text/html" },
            { "xml", "text/xml" },
            { "txt", "text/plain" },
            { "css", "text/css" },
            { "png", "image/png" },
            { "gif", "image/gif" },
            { "jpg", "image/jpg" },
            { "jpeg", "image/jpeg" },
            { "zip", "application/zip"}
        };

        public bool Start(IPAddress ipAddress, int port, int maxNOfCon, string contentPath)
        {
            if (running)
            {
                return false;
            }

            try
            {
                serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                serverSocket.Bind(new IPEndPoint(ipAddress, port));
                serverSocket.Listen(maxNOfCon);
                serverSocket.ReceiveTimeout = timeout;
                serverSocket.SendTimeout = timeout;
                running = true;
                this.contentPath = contentPath; 
            }
            catch
            {
                return false;
            }

            Thread requestListenerT = new Thread(() =>
                {
                    while (running)
                    {
                        Socket clientSocket;

                        try
                        {
                            clientSocket = serverSocket.Accept();
                            Thread requestHandler = new Thread(() =>
                                {
                                    clientSocket.ReceiveTimeout = timeout;
                                    clientSocket.SendTimeout = timeout;
                                    try { handleTheRequest(clientSocket); }
                                    catch
                                    {
                                        try { clientSocket.Close(); }
                                        catch { }
                                    }
                                });
                            requestHandler.Start();
                        }
                        catch { }
                    }
                });
            requestListenerT.Start();

            return true;
        }

        public void Stop()
        {
            if (running)
            {
                running = false;
                try { serverSocket.Close(); }
                catch { }
                serverSocket = null;
            }
        }

        
        private void notImplemented(Socket clientSocket)
        {
            sendResponse(clientSocket, "<html><head></head><body><h2>CoderoCrank WebServer</h2><div>501 - Metoda nie została zaimplementowana</div></body></html>", "501 Nie zaimplementowano", "text/html");
        }

        private void notFound(Socket clientSocket)
        {
            sendResponse(clientSocket, "<html><head></head><body><h2>CoderoCrank Simple WebServer</h2>404 - Nie znaleziono strony !!!</body></html>", "404 Nie znaleziono strony", "text/html");
        }

        private void sendOkResponse(Socket clientSocket, byte[] bContent, string contentType)
        {
            sendResponse(clientSocket, bContent, "200 OK", contentType);
        }

        private void sendResponse(Socket clientSocket, string strContent, string responseCode, string contentType)
        {
            byte[] bContent = charEncoder.GetBytes(strContent);
            sendResponse(clientSocket, bContent, responseCode, contentType);
        }

        private void sendResponse(Socket clientSocket, byte[] bContent, string responseCode, string contentType)
        {
            try
            {
                byte[] bHeader = charEncoder.GetBytes(
                                    "HTTP/1.1 " + responseCode + "\r\n"
                                  + "Server: Codero Http Web Server\r\n"
                                  + "Content-Length: " + bContent.Length.ToString() + "\r\n"
                                  + "Connection: close\r\n"
                                  + "Content-Type: " + contentType + "\r\n\r\n");
                clientSocket.Send(bHeader);
                clientSocket.Send(bContent);
                clientSocket.Close();
            }
            catch { }
        }

        private void handleTheRequest(Socket clientSocket)
        {
            byte[] buffer = new byte[1024]; 
            int receivedBCount = clientSocket.Receive(buffer); 
            string strReceived = charEncoder.GetString(buffer, 0, receivedBCount);

            string httpMethod = strReceived.Substring(0, strReceived.IndexOf(" "));

            int start = strReceived.IndexOf(httpMethod) + httpMethod.Length + 1;
            int length = strReceived.LastIndexOf("HTTP") - start - 1;
            string requestedUrl = strReceived.Substring(start, length);

            string requestedFile;
            if (httpMethod.Equals("GET") || httpMethod.Equals("POST"))
                requestedFile = requestedUrl.Split('?')[0];
            else 
            {
                notImplemented(clientSocket);
                return;
            }

            requestedFile = requestedFile.Replace("/", "\\").Replace("\\..", ""); 
            start = requestedFile.LastIndexOf('.') + 1;
            if (start > 0)
            {
                length = requestedFile.Length - start;
                string extension = requestedFile.Substring(start, length);
                if (extensions.ContainsKey(extension)) 
                    if (File.Exists(contentPath + requestedFile)) 
                        sendOkResponse(clientSocket, File.ReadAllBytes(contentPath + requestedFile), extensions[extension]);
                    else
                        notFound(clientSocket); 
            }
            else
            {
                if (requestedFile.Substring(length - 1, 1) != "\\")
                    requestedFile += "\\";
                if (File.Exists(contentPath + requestedFile + "index.htm"))
                    sendOkResponse(clientSocket, File.ReadAllBytes(contentPath + requestedFile + "\\index.htm"), "text/html");
                else if (File.Exists(contentPath + requestedFile + "index.html"))
                    sendOkResponse(clientSocket, File.ReadAllBytes(contentPath + requestedFile + "\\index.html"), "text/html");
                else
                    notFound(clientSocket);
            }
        }

    }
}
